﻿using System;
using System.Collections.Generic;
using System.Threading;
using System.Text;
using System.Net;
using System.Net.Sockets;

using Shared;

namespace WorldServer
{
    public class ClientManager
    {
        #region Fields

        /// <summary>
        /// List of clients currently connected.
        /// </summary>
        private List<Client> ClientList = new List<Client>();

        /// <summary>
        /// Syncronizing object for client list operations.
        /// </summary>
        private object Sync = new object();

        /// <summary>
        /// Client handler thread pool.
        /// </summary>
        private Thread[] ClientHandlers;

        /// <summary>
        /// Amount of handlers to work.
        /// </summary>
        private const int HandlerCount = 5;

        #endregion

        #region Constructor

        /// <summary>
        /// Constructs a new client manager.
        /// </summary>
        public ClientManager()
        {
            ClientHandlers = new Thread[ClientManager.HandlerCount];

            try
            {
                for (int i = 0; i < ClientHandlers.Length; i++)
                {
                    ClientHandlers[i] = new Thread(new ThreadStart(this.HandlerProcess));
                    ClientHandlers[i].Start();
                }
            }
            catch
            {
                throw new Exception("Failed to initialize Client Handler.");
            }
        }

        #endregion

        #region Methods

        /// <summary>
        /// Adds a client to the client list.
        /// </summary>
        /// <param name="Client">Client to be added.</param>
        public void AddClient(Client Client)
        {
            lock (this.Sync)
            {
                this.ClientList.Add(Client);
            }
        }

        /// <summary>
        /// Deletes a client from the client list.
        /// </summary>
        /// <param name="Client">Client to be deleted.</param>
        public void DeleteClient(Client Client)
        {
            lock (this.Sync)
            {
                this.ClientList.Remove(Client);
            }
        }

        /// <summary>
        /// Handles an incoming connection from the program network handler.
        /// </summary>
        /// <param name="Result">AsyncResult from the TcpListener.</param>
        public void HandleConnection(IAsyncResult Result)
        {
            try
            {
                Socket ClientSocket = Program.Network.Listener.EndAcceptSocket(Result);
                Program.Network.Standby();

                Program.ClientManager.AddClient(new Client(ClientSocket));
            }
            catch (Exception e)
            {
                Program.Logger.Write(Shared.LogType.Warning, "Failed to handle client: {0}", e.Message);
            }
        }

        /// <summary>
        /// Process method for the client handler threads.
        /// </summary>
        private void HandlerProcess()
        {
            try
            {
                List<Client> ReadyClients = new List<Client>();

                while (true)
                {
                    ReadyClients.Clear();

                    lock (this.Sync)
                    {
                        for (int i = 0; i < this.ClientList.Count; i++)
                        {
                            if (!this.ClientList[i].IsBusy)
                            {
                                this.ClientList[i].IsBusy = true;

                                if (this.ClientList[i].ClientSocket.Poll(100, SelectMode.SelectRead))
                                {
                                    ReadyClients.Add(this.ClientList[i]);
                                }
                                else
                                {
                                    ClientList[i].IsBusy = false;
                                }
                            }
                        }
                    }

                    while (ReadyClients.Count > 0)
                    {
                        byte[] Buffer = new byte[1500];

                        try
                        {
                            if (ReadyClients[0].ClientSocket.Receive(Buffer) < 1)
                            {
                                throw new Exception("Disconnect");
                            }
                        }
                        catch (Exception e)
                        {
                            if (e.Message == "Disconnect" || !ReadyClients[0].ClientSocket.Connected)
                            {
                                ReadyClients[0].Destruct("Socket not connected.");
                                ReadyClients.RemoveAt(0);

                                continue;
                            }
                            else
                            {
                                Program.Logger.Write(Shared.LogType.Warning, "Failed to receive packet: {0}", e.Message);
                            }
                        }

                        DataPacket[] Packets = DataPacket.SplitNaglePackets(Buffer);

                        for (int i = 0; i < Packets.Length; i++)
                        {
                            ReadyClients[0].HandlePacket(Packets[i]);
                        }

                        ReadyClients[0].IsBusy = false;
                        ReadyClients.RemoveAt(0);
                    }

                    Thread.Sleep(50);
                }
            }
            catch (Exception e)
            {
                Program.Logger.Crash(e);
            }
        }

        #endregion
    }
}
